Packages R sur BioConductor

Récupération de la liste de packages

Une liste des packages est disponible sur la page : http://bioconductor.org/packages/release/BiocViews.html#___Software

Cette liste reprend l'ensemble des paquets disponibles, avec leur nom, leurs maintainers ainsi qu'une courte description. Il est très simple d'extraire manuellement la liste des packages avec un simple copier-coller, et d'ensuite de parser le résultat. On peut également automatiser ce processus. Il y a cependant un détail important : la liste générée sur la page l'est via du Javascript, ce qui implique qu'on ne peut pas "simplement" ouvrir la page distante et parser son contenu.

Un rapide petit tour dans la source de la page permet de voir que les données sont en réalité extraites depuis un fichier packages.js (http://bioconductor.org/packages/json/3.0/bioc/packages.js). Ce fichier contient une déclaration de variable Javascript data_annotation_packages dont la valeur est un dictionnaire des packages disponibles. Vu que c'est essentiellement du JSON, on peut directement le parser avec Python :


In [21]:
import requests
import json
import BeautifulSoup as bs


URL = 'http://bioconductor.org/packages/json/3.0/bioc/packages.js'
PKG_URL = 'http://bioconductor.org/packages/release/bioc/html/{}.html'
OUTPUT_FILENAME = '../data/bioconductor_description.csv'

"""
URL = 'http://bioconductor.org/packages/json/3.0/data/annotation/packages.js'
PKG_URL = 'http://bioconductor.org/packages/release/data/annotation/html/{}.html'
OUTPUT_FILENAME = '../data/bioconductor_annotation_description.csv'


URL = 'http://bioconductor.org/packages/json/3.0/data/experiment/packages.js'
PKG_URL = 'http://bioconductor.org/packages/release/data/experiment/html/{}.html'
OUTPUT_FILENAME = '../data/bioconductor_experiment_description.csv'
"""

content = requests.get(URL).content

# Remove variable declaration
_, content = content[:-1].split(' = ', 1) 

# JSON
content = json.loads(content)

La structure obtenue est assez simple : une clé "content" qui contient une liste des entrées. Chaque entrée est alors une liste de 3 éléments : un nom de package, une liste de maintainers et une courte description.


In [22]:
content['content'][:3]


Out[22]:
[[u'a4',
  u'Tobias Verbeke <tobias.verbeke@openanalytics.eu>, Willem Ligtenberg <willem.ligtenberg@openanalytics.eu>',
  u'Automated Affymetrix Array Analysis Umbrella Package'],
 [u'a4Base',
  u'Tobias Verbeke <tobias.verbeke@openanalytics.eu>, Willem Ligtenberg <willem.ligtenberg@openanalytics.eu>',
  u'Automated Affymetrix Array Analysis Base Package'],
 [u'a4Classif',
  u'Tobias Verbeke <tobias.verbeke@openanalytics.eu>, Willem Ligtenberg <willem.ligtenberg@openanalytics.eu>',
  u'Automated Affymetrix Array Analysis Classification Package']]

On peut donc aisément obtenir la liste des noms de packages présents sur Bioconductor :


In [23]:
packages = map(lambda x: x[0], content['content'])

In [24]:
packages[:10]


Out[24]:
[u'a4',
 u'a4Base',
 u'a4Classif',
 u'a4Core',
 u'a4Preproc',
 u'a4Reporting',
 u'ABarray',
 u'ABSSeq',
 u'aCGH',
 u'ACME']

Récupération des fichiers DESCRIPTION des packages

Pour chaque package de nom NAME, une page http://bioconductor.org/packages/release/bioc/html/NAME.html est disponible. Par exemple, pour le paquet a4, la page http://bioconductor.org/packages/release/bioc/html/a4.html reprend une série d'informations concernant l'installation et l'usage du paquet. En particulier, cette page référence aussi, dans sa section Details, une série de couples clé/valeur correspondant aux entrées du fichier DESCRIPTION que l'on retrouve dans les packages R.

Nous allons utiliser BeautifulSoup pour parser la page et récupérer cette information.


In [25]:
def description_for_package(package_name):
    """
    Given a R package name on BioConductor, return a dictionary that contains 
    every key -> value that can be found in the "Details" section of the 
    related package page (PKG_URL).
    """
    try:
        content = requests.get(PKG_URL.format(package_name)).content
        soup = bs.BeautifulSoup(content)
        table = soup.find(name='table', attrs={'class': 'details'})
        data = {}
        for row in table.findChildren('tr'):
            key, value = row.findChildren('td')
            data[key.text] = value.text
        return data
    except Exception: 
        print 'Exception while working on', package_name
        raise

Appliquons cette fonction sur tous les noms de packages repris dans la liste packages de l'étape précédente, et récupérons les résultats. Nous plaçons ces résultats dans un dictionnaire, où la clé du dictionnaire est le nom du package, et la valeur de ce dictionnaire est l'information structurée récupérée depuis le site.


In [26]:
packages_data = {}

for package in packages:
    data = description_for_package(package)
    packages_data[package] = data

On peut maintenant facilement exporter ça vers un fichier .csv qui sera réutilisé plus tard.


In [27]:
import pandas

pandas.DataFrame.from_dict(packages_data, orient='index').to_csv(OUTPUT_FILENAME)